package bussiness

import (
	"GCodeStencil/model"
	. "GCodeStencil/util"
	"bufio"
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	. "gitee.com/tomatomeatman/golang-repository/bricks/model"
	//. "gitee.com/tomatomeatman/golang-repository/bricks/utils/function/data"
	. "gitee.com/tomatomeatman/golang-repository/bricks/utils/function/file"
	Log "github.com/cihub/seelog"
)

type CodeStencilService struct {
}

type CreateCodeInfo struct {
	GdbSequence    string              `json:"dbSequence"`
	GtableInfoList []string            `json:"tableInfoList"`
	Gtemplets      []string            `json:"templets"`
	GcustomInfo    []map[string]string `json:"customInfo"` //模板列表
	GdbInfo        model.DBInfo        `json:"dbInfos"`
	GsCode         string              `json:"sCode"` //特定代码
}

/**
 * 创建代码
 */
func (this CodeStencilService) CreateCode(sUserId string, param CreateCodeInfo) *MsgEmity {
	if nil == &param {
		return MsgEmity{}.Err(8001, "参数获取失败!")
	}

	// if nil == &param.GdbInfos {
	// 	return MsgEmity{}.Err(8002, "数据库参数错误!")
	// }

	if "" == param.GdbSequence {
		return MsgEmity{}.Err(8003, "数据库序列错误!")
	}

	if nil == param.Gtemplets {
		return MsgEmity{}.Err(8004, "模板参数错误!")
	}

	if nil == param.GcustomInfo {
		return MsgEmity{}.Err(8005, "自定义参数错误!")
	}

	path := "./temp/db/" + sUserId + "/" + param.GdbSequence + ".json"
	f, err := ioutil.ReadFile(path)
	if err != nil {
		return MsgEmity{}.Err(8006, "读取失败")
	}

	err = json.Unmarshal(f, &(param.GdbInfo))
	if err != nil {
		//Log.Error("Json字符串转换异常: %+v\n", err)
		return MsgEmity{}.Err(8007, "数据库信息转换异常")
	}

	customInfo := map[string]string{}
	for _, m := range param.GcustomInfo {
		customInfo[m["sName"]] = m["sValue"]
	}

	savePath := this.appPath() + "/temp/code/" + sUserId + "/" + time.Now().Format("20060102150405")
	data := map[string]interface{}{
		"dbInfo":   param.GdbInfo,
		"sequence": param.GdbSequence,
		//"tableInfo":     table,
		"customInfo":    customInfo,
		"tableInfoList": param.GdbInfo.GtableInfoList,
		"fileSavePath":  savePath,
	}

	logsFile := "./temp/logs/" + sUserId + "/last.log"
	os.Remove(logsFile) //删除日志文件

	iState := []int{0, 0, 0, 0} //表总数/模板总数/成功数/错误数
	for _, table := range param.GdbInfo.GtableInfoList {
		hav := false
		for _, v := range param.GtableInfoList {
			if table.GsName == v {
				hav = true
				break
			}
		}

		if !hav {
			continue
		}

		data["tableInfo"] = table
		iState[0] = iState[0] + 1

		copyPaths := []string{}
		for _, file := range param.Gtemplets {
			if !(FileUtil{}.IsFile(file)) {
				if strings.HasSuffix(file, "/copy") || strings.HasSuffix(file, "/copy/") {
					bl := false
					for _, v := range copyPaths {
						if v == file {
							bl = true
						}
					}

					if !bl {
						copyPaths = append(copyPaths, file)
					}
				}

				continue
			}

			isCopyFile := false
			for _, v := range copyPaths {
				if !strings.HasPrefix(file, v) { //属于复制目录
					continue
				}

				isCopyFile = true
				this.createCopyFile(file, data)
			}

			if isCopyFile {
				continue
			}

			f, err := ioutil.ReadFile(file)
			if err != nil {
				return MsgEmity{}.Err(8007, "读取模板失败")
			}

			iState[1] = iState[1] + 1

			me := TemplateUtil{}.CreateCode(sUserId, file, string(f), data)
			if !me.Gsuccess {
				Log.Error("使用模板'", file, "'发生错误:", me.Gmsg)
				this.writeLogs("使用模板'"+file+"'发生错误:"+me.Gmsg, logsFile)
				iState[3] = iState[3] + 1
				continue
			}

			iState[2] = iState[2] + 1
			this.save(savePath, me.Gdata.(string), sUserId)
		}
	}

	return MsgEmity{}.Success(iState, "创建成功")
}

/**
 * 测试代码
 */
func (this CodeStencilService) ValidCode(sUserId string, param CreateCodeInfo) *MsgEmity {
	if nil == &param {
		return MsgEmity{}.Err(8001, "参数获取失败!")
	}

	// if nil == &param.GdbInfos {
	// 	return MsgEmity{}.Err(8002, "数据库参数错误!")
	// }

	if "" == param.GdbSequence {
		return MsgEmity{}.Err(8003, "数据库序列错误!")
	}

	if nil == param.Gtemplets {
		return MsgEmity{}.Err(8004, "模板参数错误!")
	}

	if nil == param.GcustomInfo {
		return MsgEmity{}.Err(8005, "自定义参数错误!")
	}

	path := "./temp/db/" + sUserId + "/" + param.GdbSequence + ".json"
	f, err := ioutil.ReadFile(path)
	if err != nil {
		return MsgEmity{}.Err(8006, "读取失败")
	}

	err = json.Unmarshal(f, &(param.GdbInfo))
	if err != nil {
		//Log.Error("Json字符串转换异常: %+v\n", err)
		return MsgEmity{}.Err(8007, "数据库信息转换异常")
	}

	customInfo := map[string]string{}
	for _, m := range param.GcustomInfo {
		customInfo[m["sName"]] = m["sValue"]
	}

	savePath := "/temp/code/" + sUserId + "/" + time.Now().Format("20060102150405")
	data := map[string]interface{}{
		"dbInfo":   param.GdbInfo,
		"sequence": param.GdbSequence,
		//"tableInfo":     table,
		"customInfo":    customInfo,
		"tableInfoList": param.GdbInfo.GtableInfoList,
		"fileSavePath":  savePath,
	}

	var result strings.Builder

	for _, table := range param.GdbInfo.GtableInfoList {
		hav := false
		for _, v := range param.GtableInfoList {
			if table.GsName == v {
				hav = true
				break
			}
		}

		if !hav {
			continue
		}

		data["tableInfo"] = table

		me := TemplateUtil{}.CreateCode(sUserId, "测试代码", param.GsCode, data)
		if !me.Gsuccess {
			result.WriteString("-------------- ")
			result.WriteString(time.Now().Format("2006-01-02 15:04:05"))
			result.WriteString(" --------------\r\n\r\n")
			result.WriteString("测试代码存在错误:" + me.Gmsg)
			result.WriteString("\r\n\r\n")
			continue
		}

		result.WriteString("-------------- ")
		result.WriteString(time.Now().Format("2006-01-02 15:04:05"))
		result.WriteString(" --------------\r\n\r\n")
		result.WriteString(me.Gdata.(string))
		result.WriteString("\r\n\r\n")
	}

	return MsgEmity{}.Success(result.String(), "调试结束")
}

// 写日志
func (this CodeStencilService) writeLogs(txt, filePath string) {
	dir := filepath.Join(filePath, "../") //获得上级文件夹路径
	os.MkdirAll(dir, 0777)                // 创建文件夹

	file, _ := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) //文件可能不存在

	//及时关闭file句柄
	defer file.Close()

	//写入文件时，使用带缓存的 *Writer
	write := bufio.NewWriter(file)
	write.WriteString(txt + "\r\n")

	write.Flush() //Flush将缓存的文件真正写入到文件中
}

/**
 * 复制指定为'复制'的文件
 * @param filePath
 * @param dataMap
 */
func (this CodeStencilService) createCopyFile(filePath string, dataMap map[string]interface{}) {
	savePath := strings.Replace(filePath, "\\\\", "/", -1)
	iSt := strings.Index(savePath, "/copy/")
	savePath = savePath[iSt+len("/copy/"):]

	tableInfo := dataMap["tableInfo"].(model.TableInfo)

	savePath = dataMap["fileSavePath"].(string) + "/" + savePath
	savePath = strings.Replace(savePath, "${tableInfo.sDbCodeName}", tableInfo.GsDbCodeName, -1)
	savePath = strings.Replace(savePath, "${tableInfo.sDbCodeSimplName}", tableInfo.GsDbCodeSimplName, -1)
	savePath = strings.Replace(savePath, "${tableInfo.sName}", tableInfo.GsEntityName, -1)
	savePath = strings.Replace(savePath, "${tableInfo.sLowerName}", tableInfo.GsLowerName, -1)
	savePath = strings.Replace(savePath, "//", "/", -1)
	savePath = strings.Replace(savePath, "\\\\", "/", -1)

	_, err := os.Lstat(savePath) //如果某个文件不存在，那么使用os.Lstat就一定会返回error
	if nil == err {
		return //已经存在则不进行保存,因为每次创建代码文件都会在不同时间主目录
	}

	_, err = this.copyFile(filePath, savePath)
	if nil != err {
		Log.Error("复制文件'", filePath, "'发生异常:", err.Error())
	}
}

func (this CodeStencilService) save(rootPath, text, sUserId string) {
	iSt := strings.Index(text, "\r\n")
	lientSign := 2 //换行符

	if iSt < 0 {
		iSt = strings.Index(text, "\n")
		lientSign = 1
	}

	var path string
	if iSt > 0 {
		path = text[:iSt]
		text = text[iSt+lientSign:]
	} else {
		path = rootPath + "/error/" + time.Now().Format("20060102150405") + ".txt"
	}

	_, err := os.Lstat(path) //如果某个文件不存在，那么使用os.Lstat就一定会返回error
	if nil == err {
		return //已经存在则不进行保存,因为每次创建代码文件都会在不同时间主目录
	}

	FileUtil{}.Save(text, path)
}

/**
 * 创建代码
 */
func (this CodeStencilService) Down(filePaht string) {

}

// 获取当前执行程序所在的绝对路径
func (this CodeStencilService) appPath() string {
	exePath, err := os.Executable()
	if err != nil {
		return "./"
	}

	res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))

	return res
}

func (this CodeStencilService) copyFile(srcFile, destFile string) (int64, error) {
	file1, err := os.Open(srcFile)
	if err != nil {
		Log.Error("打开源文件'", srcFile, "'发生异常:", err.Error())
		return 0, err
	}

	dir := filepath.Join(destFile, "../") //获得上级文件夹路径
	os.MkdirAll(dir, 0777)                // 创建文件夹

	file2, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, os.ModePerm)
	if err != nil {
		Log.Error("创建目标文件'", destFile, "'发生异常:", err.Error())
		return 0, err
	}

	defer file1.Close()
	defer file2.Close()

	return io.Copy(file2, file1)
}
